/*
 * Tablero.cpp
 *
 *      Author: sebastian
 */

#include "Tablero.h"

Tablero::Tablero(Json::Value& root){
	cantFilas = root["cantFilas"].asUInt();
	cantColumnas = root["cantColumnas"].asUInt();
	tablero = new Celda[cantFilas*cantColumnas];
	cargaGeneradoresColumnas(root);
	cargaCeldasIniciales(root);
}

void Tablero::cargaGeneradoresColumnas(Json::Value& root){
	for (unsigned int i = 0; i < cantColumnas ; i++){
		std::stringstream aux;
		aux << "Columna " << i;
		generadorColumna.push_back(new GeneradorCelda(root["PGeneradores"][aux.str()]));
	}
}

void Tablero::cargaCeldasIniciales(Json::Value& root){
	srand(time(NULL));
	unsigned int contador = 0;
	for (unsigned int i = 0; i<cantFilas ; i++){
		for (unsigned int j= 0 ; j < cantColumnas ; j++){
			std::stringstream s2;
			s2 << "Celda " << contador;
			if(root["PCeldas"][s2.str()]["esAgujero"].asBool()){
				GeneradorCelda generador;
				getCelda(i,j) = generador.generarAgujero();
			} else {
				GeneradorCelda generador(root["PCeldas"][s2.str()]) ;
				getCelda(i,j) = generador.generarCelda();
			}
			contador++;
		}
	}

}

Tablero::Tablero(const Tablero& tablero){
	this->cantFilas = tablero.cantFilas;
	this->cantColumnas = tablero.cantColumnas;
	this->tablero = new Celda[cantFilas*cantColumnas];
	memcpy(this->tablero,tablero.tablero,cantColumnas*cantFilas*sizeof(Celda));
}

Tablero::~Tablero() {
	if (tablero != 0) delete[] tablero;
	for (unsigned int i = 0; i < generadorColumna.size() ; i++){
		delete generadorColumna[i];
	}
}


void Tablero::dibujarTablero() const{
	for (unsigned int i = 0; i<cantFilas ; i++){
		for (unsigned int j= 0 ; j < cantColumnas ; j++){
			//filas * columnas + columnas
			this->tablero[i*cantColumnas +j].dibujarse();
			std::cout << " ";
		}
		std::cout << std::endl;
	}

}

Celda& Tablero::getCelda(int fila, int columna){
	return tablero[fila*cantColumnas + columna];
}

void Tablero::intercambiarDosCeldas(int filaInicial,int colInicial, int filaFinal,int colFinal){
	if (filaInicial >= 0 && filaFinal < (int) cantFilas &&
			colInicial >=0 && colFinal < (int) cantColumnas){
		Celda aux = getCelda(filaFinal,colFinal);
		tablero[filaFinal*cantColumnas+colFinal] = getCelda(filaInicial,colInicial);
		tablero[filaInicial*cantColumnas+colInicial] = aux;
	}
}

int Tablero::comprobarConDimensiones(int filaInicial, int colInicial, int filaFinal, int colFinal){
	if (filaInicial < 0 || filaInicial >= (int) cantFilas ||
			filaFinal < 0 || filaFinal >= (int)cantFilas) {
		return -1;
	}
	if (colInicial < 0 || colInicial >= (int)cantColumnas ||
			colFinal < 0 || colFinal >= (int)cantColumnas) {
		return 1;
	}
	return 0;
}

int Tablero::comprobarAdyacencia(int filaInicial, int colInicial, int filaFinal, int colFinal){
	int diferenciaFila = filaInicial - filaFinal;
	int diferenciaCol = colInicial - colFinal;

	//no se puede mover a si mismo
	if (diferenciaFila==0 && diferenciaCol==0) {
		return -1;
	}

	//No se pueden mover salvo que sean adyacentes
	if (diferenciaFila==0){
		if (diferenciaCol > 1 || diferenciaCol < -1 || diferenciaCol == 0){
			return -1;
		}
	} else if (diferenciaCol==0){
		if (diferenciaFila > 1 || diferenciaFila < -1 || diferenciaFila == 0){
			return -1;
		}
	} else {
		//moviento que no tiene misma fila o misma columna, saltea gemas.
		return 1;
	}
	return 0;
}

int Tablero::mover(int filaInicial, int colInicial , int filaFinal, int colFinal){
	//compruebo que sea dentro de los rangos que se puede mover
	if (comprobarConDimensiones(filaInicial,colInicial,filaFinal,colFinal)!=0){
		return 0;
	}

	if(comprobarAdyacencia(filaInicial, colInicial, filaFinal, colFinal)!=0){
		return 0;
	}

	//Compruebo que lo que se quiera mover no sea un Agujero
	if (getCelda(filaInicial,colInicial).esAgujero() ||
			getCelda(filaFinal,colFinal).esAgujero()){
		return 0;
	}

	intercambiarDosCeldas(filaInicial,colInicial,filaFinal,colFinal);

	int puntaje = 0;

	//Casos particulares
	puntaje = especiales(filaInicial,colInicial,filaFinal,colFinal);
	if (puntaje > 0) return puntaje;

	puntaje = especiales(filaFinal,colFinal, filaInicial,colInicial);
	if (puntaje > 0) return puntaje;

	/***************************/
	puntaje += this->comprobarMatch(filaFinal,colFinal,ORIENTACION_HORIZONTAL);
	puntaje +=this->comprobarMatch(filaFinal,colFinal,ORIENTACION_VERTICAL);
	puntaje +=this->comprobarMatch(filaInicial,colInicial,ORIENTACION_HORIZONTAL);
	puntaje +=this->comprobarMatch(filaInicial,colInicial,ORIENTACION_VERTICAL);

	if (puntaje <= 0) intercambiarDosCeldas(filaFinal,colFinal,filaInicial,colInicial);
	return puntaje;
}

int Tablero::especiales(int filaInicial, int colInicial, int filaFinal, int colFinal){
	int puntaje = 0;
	if (this->getCelda(filaInicial,colInicial).esEstrella()){
		if (this->getCelda(filaFinal,colFinal).esEstrella()){
			puntaje = PUNTAJEUNIDADESPECIAL*this->marcarEliminarTablero();
			return puntaje;
		} else if (this->getCelda(colFinal,filaFinal).esMinibar()){
			//borra mismo color + fila + columna
			puntaje = PUNTAJEUNIDADESPECIAL*this->marcarEliminarDadoUnTipo(getCelda(filaFinal,colFinal).getTipo());
			puntaje+= PUNTAJEUNIDADESPECIAL*marcarEliminarFila(filaFinal);
			puntaje+= PUNTAJEUNIDADESPECIAL*marcarEliminarColumna(colFinal);
			return puntaje;
		} else {
			//borrar todos los del mismo color de filaFinal,colFinal
			getCelda(filaInicial,colInicial).cambiarEstado();
			puntaje = PUNTAJEUNIDADESPECIAL*this->marcarEliminarDadoUnTipo(getCelda(filaFinal,colFinal).getTipo());
			return puntaje;
		}
	}
	return 0;
}

int Tablero::comprobarMatch(int fila, int columna, int orientacion){
	std::vector<Celda*> vector;

	if (orientacion == ORIENTACION_VERTICAL){
		vector = this->hayMatchVertical(fila,columna);

	} else if (orientacion == ORIENTACION_HORIZONTAL){
		vector = this->hayMatchHorizontal(fila,columna);

	}
	if (vector.size() < 3) return 0;

	int puntajePorUnidad = 0;
	int cantidadDeMatcheados = 0;
	cantidadDeMatcheados += comprobarMiniBarHorizontal(vector);
	cantidadDeMatcheados += comprobarMiniBarvertical(vector);

	if (vector.size() == 3) {
		marcarABorrar(vector);
		puntajePorUnidad= PUNTAJEUNIDADMATCH3;
		cantidadDeMatcheados += vector.size();
	}

	if (vector.size() ==4){
		int tipo = getCelda(fila,columna).getTipo();
		if (orientacion == ORIENTACION_VERTICAL){
			tablero[fila*cantColumnas+columna] = generador.generarMiniBarVertical(tipo);
		} else if (orientacion == ORIENTACION_HORIZONTAL){
			tablero[fila*cantColumnas+columna] = generador.generarMiniBarHorizontal(tipo);
		}
		tablero[fila*cantColumnas+columna].setearPosicion(fila,columna);
		cantidadDeMatcheados += vector.size();
		vector.erase(vector.begin());
		marcarABorrar(vector);
		puntajePorUnidad = PUNTAJEUNIDADMATCH4;
	}

	if ( vector.size() >= 5 ){
		tablero[fila*cantColumnas+columna] = generador.generarEstrella();
		tablero[fila*cantColumnas+columna].setearPosicion(fila,columna);
		cantidadDeMatcheados += vector.size();
		vector.erase(vector.begin());
		marcarABorrar(vector);
		puntajePorUnidad = PUNTAJEUNIDADMAMATCHMASDE5;
	}
	return cantidadDeMatcheados*puntajePorUnidad;
}


std::vector<Celda*> Tablero::hayMatchHorizontal(int fila, int columna){
	std::vector<Celda*> matchHorizontal;
	matchHorizontal.push_back(&tablero[fila*cantColumnas+columna]);

	//horizontales para atras
	unsigned int tipo = getCelda(fila,columna).getTipo();
	if (tipo != 0){
		int auxcolumna = columna - 1;

		while (auxcolumna >= 0 && tipo == getCelda(fila,auxcolumna).getTipo() ){
			matchHorizontal.push_back(&getCelda(fila,auxcolumna));
			auxcolumna -= 1;
		}

		//Para adelante
		auxcolumna = columna+1 ;

		while (auxcolumna < (int) cantColumnas && tipo == getCelda(fila,auxcolumna).getTipo()){
			matchHorizontal.push_back(&getCelda(fila,auxcolumna));
			auxcolumna += 1;
		}
	}
	return matchHorizontal;
}

std::vector<Celda*> Tablero::hayMatchVertical(int fila, int columna){

	std::vector<Celda*> matchVertical;

	matchVertical.push_back(&getCelda(fila,columna));
	unsigned int tipo = getCelda(fila,columna).getTipo();
	if (tipo != 0){
		int auxfila = fila-1;
		//para arriba
		while (auxfila >= 0 && tipo == getCelda(auxfila,columna).getTipo() ){
			matchVertical.push_back(&getCelda(auxfila,columna));
			auxfila -= 1;
		}

		//Para abajo
		auxfila = fila+1 ;

		while (auxfila < (int)cantFilas && tipo == getCelda(auxfila,columna).getTipo()){
			matchVertical.push_back(&getCelda(auxfila,columna));
			auxfila += 1;
		}
	}
	return matchVertical;
}


void Tablero::marcarABorrar(std::vector<Celda*>& vector){
	for (unsigned int i = 0; i< vector.size() ; i++){
		vector[i]->cambiarEstado();
	}
}

void Tablero::bajarCeldas(){
	int columnaActual = 0;
	int filaActual = cantFilas - 2; //empiezo desde la anteultima
	while (filaActual >= 0){
		while (columnaActual < (int) cantColumnas){
			bool agujero = getCelda(filaActual,columnaActual).esAgujero();
			bool estado = getCelda(filaActual,columnaActual).getEstado();
			int auxFila = filaActual;
			if (!agujero && estado) {       //si no es un Agujero y es valida
				while (auxFila+1 < (int) cantFilas &&
						(!getCelda(auxFila+1,columnaActual).getEstado() ||
								getCelda(auxFila+1,columnaActual).getTipo()==0)) {
					auxFila++;
				}
				while (filaActual < auxFila &&getCelda(auxFila,columnaActual).getTipo()==0)
					auxFila--;
				intercambiarDosCeldas(filaActual,columnaActual,auxFila,columnaActual);
			}
			columnaActual++;
		}
		columnaActual=0;
		filaActual--;
	}
}

bool Tablero::hayMasMovimientos(){
	bool encontrado = false;
	Tablero* copia = new Tablero(*this);
	unsigned int colInicial = 0;
	unsigned int colFinal = colInicial+1;
	unsigned int filaInicial = 0;

	//Si hay una estrella, hay un movimiento.
	int fila = 0;
	int columna = 0;

	while (fila < (int) cantFilas ){
		while (columna < (int) cantColumnas){
			if( getCelda(fila,columna).esEstrella()){
				encontrado=true;
			}
			columna++;
		}
		columna=0;
		fila++;
	}
	/*************************************/
	//comparaciones horizontales
	while (filaInicial<cantFilas && !encontrado){
		while (colFinal<cantColumnas && !encontrado ){
			if (copia->mover(filaInicial,colInicial,filaInicial,colFinal)>0){
				encontrado = true;
			}
			colInicial++;
			colFinal++;
		}
		colInicial= 0;
		colFinal = colInicial+1;
		filaInicial++;
	}

	if (!encontrado) {
		//Por verticales
		colInicial = 0;
		filaInicial = 0;
		unsigned int filaFinal = filaInicial+1;
		while (colInicial<cantColumnas && !encontrado){
			while (filaFinal<cantFilas && !encontrado ){
				if (copia->mover(filaInicial,colInicial,filaFinal,colInicial)>0){
					encontrado = true;
				}
				filaInicial++;
				filaFinal++;
			}
			filaInicial= 0;
			filaFinal = filaInicial+1;
			colInicial++;
		}

	}
	delete copia;
	return encontrado;
}

void Tablero::recargarVacios(){
	for (unsigned int i = 0; i<cantFilas ; i++){
		for (unsigned int j= 0 ; j < cantColumnas ; j++){
			Celda celda = getCelda(i,j);
			if (!celda.getEstado()){
				tablero[i*cantColumnas+j] = generadorColumna[j]->generarCelda();
				tablero[i*cantColumnas+j].setearPosicion(i,j);
			}
		}
	}
}


int Tablero::marcarEliminarFila(int fila){
	std::vector<Celda*> vector;
	for (int i = 0 ; i < (int) cantColumnas ; i++){
		if (!this->getCelda(fila,i).esAgujero())
			vector.push_back(&this->getCelda(fila,i));
	}
	this->marcarABorrar(vector);
	return vector.size();
}

int Tablero::marcarEliminarColumna(int columna){
	std::vector<Celda*> vector;
	for (int i = 0 ; i < (int) cantFilas ; i++){
		if (!this->getCelda(i,columna).esAgujero())
			vector.push_back(&this->getCelda(i,columna));
	}
	this->marcarABorrar(vector);
	return vector.size();
}

int Tablero::marcarEliminarTablero(){
	unsigned int contador = 0;
	for (int i = 0; i < (int) (cantFilas*cantColumnas) ; i++){
		if (!tablero[i].esAgujero()){
			tablero[i].cambiarEstado();
			contador++;
		}
	}
	return contador;
}

int Tablero::buscarMatchLuegoDeMover(){
	int fila = 0;
	int columna = 0;
	int resultado = 0;
	while (fila < (int) cantFilas){
		while (columna < (int) cantColumnas){
			resultado += comprobarMatch(fila,columna,ORIENTACION_VERTICAL);
			if (resultado == 0){
				resultado += comprobarMatch(fila,columna,ORIENTACION_HORIZONTAL);
			}
			columna++;
		}
		columna=0;
		fila++;
	}
	return resultado;
}

int Tablero::marcarEliminarDadoUnTipo(unsigned int tipo){
	int fila = 0;
	int columna = 0;
	int contador = 0;
	while ( fila < (int) cantFilas ){
		while (columna < (int) cantColumnas){
			if(getCelda(fila,columna).getTipo()==tipo){
				getCelda(fila,columna).cambiarEstado();
				contador++;
			}
			columna++;
		}
		columna=0;
		fila++;
	}
	return contador;
}

int Tablero::comprobarMiniBarHorizontal(std::vector<Celda*>& vector){
	int matcheados = 0;
	for (unsigned int i = 0; i < vector.size() ; i++){
		Celda aux = *(vector[i]);
		if (aux.esMiniBarHorizontal()){
			matcheados += marcarEliminarFila(aux.getFila());
		}
	}

	return matcheados;
}

int Tablero::comprobarMiniBarvertical(std::vector<Celda*>& vector){
	int matcheados = 0;
	for (unsigned int i = 0; i < vector.size() ; i++){
		Celda aux = *(vector[i]);
		if (aux.esMiniBarVertical()){
			matcheados += marcarEliminarColumna(aux.getColumna());
		}
	}

	return matcheados;
}


Celda* Tablero::getTab(){
	return tablero;
}

int Tablero::getCantidadFilas(){
	return cantFilas;
}

int Tablero::getCantidadColumnas(){
	return cantColumnas;
}


Json::Value Tablero::getJson(){
	Json::Value root;
	root["cantFilas"] = cantFilas;
	root["cantColumnas"] = cantColumnas;
	unsigned int contador = 0;
	for (unsigned int i = 0; i < cantFilas ; i++){
		for (unsigned int j = 0 ; j < cantColumnas ; j++){
			std::stringstream s1;
			s1 << "Celda " << contador;
			root["tablero"][s1.str()] = getCelda(i,j).getJson();
			contador++;
		}

	}

	return root;
}

void Tablero::setJson(Json::Value& root){
	cantFilas=3;
	cantColumnas = 2;
	cantFilas = root["cantFilas"].asUInt();
	cantColumnas = root["cantColumnas"].asUInt();

	if (tablero != NULL) delete[] tablero;
	this->tablero = new Celda[cantFilas*cantColumnas];

	unsigned int contador = 0;
	for (unsigned int i = 0; i< cantFilas ; i++){
		for (unsigned int j = 0 ; j < cantColumnas ; j++){
			std::stringstream s2;
			s2 << "Celda " << contador;
			Json::Value jCelda = root["tablero"][s2.str()];
			tablero[contador].setJson(jCelda);
			contador++;
		}
	}
}
